/*
 *  drivers  sd3078
 * Copyright (C) 2022 shenzhen wave
 * version:v1.0.0--2023-2-12
 * This program is free software; you can redistribute it and/or modify
 *  Driver for sd3078 RTC 
 */
#include "sd3078.h"
#include <stdio.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "driver/i2c.h"
/*device addr*/
#define SD3078_DEV_ADR                0x64
/*time reg*/
#define SD3078_REG_SEC                0x00
#define SD3078_REG_MIN                0x01
#define SD3078_REG_HOUR               0x02
#define SD3078_REG_WDAY               0x03
#define SD3078_REG_MDAY               0x04
#define SD3078_REG_MON                0x05
#define SD3078_REG_YEAR               0x06
/*alarm reg*/
#define SD3078_REG_ALARM_SEC          0x07
#define SD3078_REG_ALARM_MIN          0x08
#define SD3078_REG_ALARM_HOUR         0x09
#define SD3078_REG_ALARM_WEEK         0x0A
#define SD3078_REG_ALARM_DAY          0x0B
#define SD3078_REG_ALARM_MONTH        0x0C
#define SD3078_REG_ALARM_YEAR         0x0D
#define SD3078_REG_ALARM_OE           0x0E
/* control reg*/
#define SD3078_REG_CTR1               0x0F
#define SD3078_REG_CTR2               0x10
#define SD3078_REG_CTR3               0x11
#define SD3078_REG_TTF                0x12
#define SD3078_REG_TD0                0x13
#define SD3078_REG_TD1                0x14
#define SD3078_REG_TD2                0x15
/*temperature reg*/
#define SD3078_REG_TEMP               0x16
/*i2c control reg*/
#define SD3078_REG_I2C_CTRL           0x17
/*charge reg*/
#define SD3078_REG_CHARGE             0x18
/*extend control reg*/
#define SD3078_REG_CTR4               0x19
#define SD3078_REG_CTR5               0x1A
/*battery voltage reg*/
#define SD3078_REG_BAT_VAL            0x1B
/*temperature low/hign alarm reg*/    
#define SD3078_REG_TEMP_AL            0x1C
#define SD3078_REG_TEMP_AH            0x1D
/*history max/min temperature reg*/
#define SD3078_REG_HIS_L              0x1E
#define SD3078_REG_HIS_H              0x1F
/*history temperature lowest  time ram*/
#define SD3078_REG_HIS_L_MIN          0x20
#define SD3078_REG_HIS_L_HOUR         0x21
#define SD3078_REG_HIS_L_WEEK         0x22
#define SD3078_REG_HIS_L_DAY          0x23
#define SD3078_REG_HIS_L_MON          0x24
#define SD3078_REG_HIS_L_YEAR         0x25
/*history temperature highest  time ram*/
#define SD3078_REG_HIS_H_MIN          0x26
#define SD3078_REG_HIS_H_HOUR         0x27
#define SD3078_REG_HIS_H_WEEK         0x28
#define SD3078_REG_HIS_H_DAY          0x29
#define SD3078_REG_HIS_H_MON          0x2A
#define SD3078_REG_HIS_H_YEAR         0x2B
/*user ram reg 2cH -71H*/
#define SD3078_REG_USER_RAM_START     0x2C
#define SD3078_REG_USER_RAM_END       0x71
/*device id 72H-79H*/
#define SD3078_REG_DEVICE_ID          0x72
#define BIT(x)  (1<< x)
/*reg bit define*/
#define SD3078_12_24_BIT              BIT(7)
#define SD3078_AM_PM_BIT			  			BIT(5)
#define SD3078_WRTC1                  BIT(7)
#define SD3078_WRTC2                  BIT(2)
#define SD3078_WRTC3                  BIT(7)

#define SD3078_TEMP_AL_OE             BIT(2)  /*temperature lowest alarm enable*/
#define SD3078_TEMP_AH_OE             BIT(3)  /*temperature lowest alarm enable*/
#define SD3078_IM                     BIT(6)
#define SD3078_INTS1                  BIT(5)
#define SD3078_INTS0                  BIT(4)
#define SD3078_ALARM_EN               BIT(7)
#define SD3078_INT_AF                 BIT(5)
#define SD3078_INTFE                  BIT(0)
#define SD3078_INTAE                  BIT(1)
#define SD3078_INTDE                  BIT(2)
#define SD3078_TDS1                   BIT(5)
#define SD3078_TDS0                   BIT(4)


#define I2C_MASTER_NUM I2C_NUM_0

#define  DRIVER_VER   " v1.0.2"

#define SD3078_DEV_ADR 0x64



static esp_err_t i2c_write_multi_bytes(unsigned char dev_addr, unsigned char reg_addr, const unsigned char *buf, unsigned short size)
{
    i2c_cmd_handle_t cmd = i2c_cmd_link_create();

    i2c_master_start(cmd);
    i2c_master_write_byte(cmd, dev_addr  | I2C_MASTER_WRITE, true);
    i2c_master_write_byte(cmd, reg_addr, true);
    i2c_master_write(cmd, buf, size, true);
    i2c_master_stop(cmd);

    esp_err_t ret = i2c_master_cmd_begin(I2C_MASTER_NUM, cmd, 1000 / portTICK_RATE_MS);
    i2c_cmd_link_delete(cmd);

    return ret;
}

static esp_err_t i2c_read_multi_bytes(unsigned char dev_addr, unsigned char reg_addr, unsigned char *buf, unsigned short size)
{
    i2c_cmd_handle_t cmd = i2c_cmd_link_create();

    i2c_master_start(cmd);
    i2c_master_write_byte(cmd, dev_addr  | I2C_MASTER_WRITE, true);
    i2c_master_write_byte(cmd, reg_addr, true);
    i2c_master_start(cmd);
    i2c_master_write_byte(cmd, dev_addr | I2C_MASTER_READ, true);
    i2c_master_read(cmd, buf, size, I2C_MASTER_LAST_NACK);
    i2c_master_stop(cmd);

    esp_err_t ret = i2c_master_cmd_begin(I2C_MASTER_NUM, cmd, 1000 / portTICK_RATE_MS);
    i2c_cmd_link_delete(cmd);

    return ret;
}

static esp_err_t sd3078_read_reg(unsigned char reg, unsigned char length, unsigned char *buf)
{
    if (i2c_read_multi_bytes(SD3078_DEV_ADR, reg, buf, length) != ESP_OK)
        return ESP_FAIL;
    return ESP_OK;
}

static esp_err_t sd3078_write_reg(unsigned char reg, unsigned char length, const unsigned char *buf)
{
    if (i2c_write_multi_bytes(SD3078_DEV_ADR, reg, buf, length) != ESP_OK)
        return ESP_FAIL;
    return ESP_OK;
}

/**
 * @brief sd3078 write  protect enable
 * 
 */
static sd3078_exit_code_e sd3078_write_enable(void) {
    uint8_t buf[2];
    sd3078_exit_code_e ret;

    ret = sd3078_read_reg(SD3078_REG_CTR1, 2, buf);
    if (ret != SD3078_EXIT_OK) {
        return ret;
    }

    buf[1] |= SD3078_WRTC1;
    ret = sd3078_write_reg(SD3078_REG_CTR2, 1, &buf[1]);
    if (ret != SD3078_EXIT_OK) {
        return ret;
    }

    buf[0] |= SD3078_WRTC2;
    buf[0] |= SD3078_WRTC3;
    ret = sd3078_write_reg(SD3078_REG_CTR1, 1, &buf[0]);
    if (ret != SD3078_EXIT_OK) {
        return ret;
    }

    return SD3078_EXIT_OK;
}

// /**
//  * @brief sd3078 write protect disable
//  * 
//  */
static sd3078_exit_code_e sd3078_write_disable(void) {
    uint8_t buf[2];
    sd3078_exit_code_e ret;

    ret = sd3078_read_reg(SD3078_REG_CTR1, 2, buf);
    if (ret != SD3078_EXIT_OK) {
        return ret;
    }

    buf[0] &= ~SD3078_WRTC2;
    buf[0] &= ~SD3078_WRTC3;
    buf[1] &= ~SD3078_WRTC1;

    ret = sd3078_write_reg(SD3078_REG_CTR1, 2, buf);
    if (ret != SD3078_EXIT_OK) {
        return ret;
    }

    return SD3078_EXIT_OK;
}

static unsigned char bcd_hex_convert(unsigned char bcd)
{
	return (bcd >> 4) * 10 + (bcd &0x0f);
}

static unsigned char hex_bcd_convert(unsigned char hex)
{
	return ((hex/10) <<4) | (hex%10);
}
/**
 * @brief sd3078 get time
 * 
 */
sd3078_exit_code_e sd3078_get_time(sd3078_time_t *time)
{
	sd3078_exit_code_e ret;
	unsigned char buf[7];

	ret = sd3078_read_reg(SD3078_REG_SEC,7, buf);
	if (ret != SD3078_EXIT_OK)
		return ret;

	time->second = bcd_hex_convert(buf[0] & 0x7f);  
	time->minute = bcd_hex_convert(buf[1] & 0x7f);  

	if ((buf[2] & SD3078_12_24_BIT) != 0)      
	{
		time->am_pm = SD3078_AM_PM_NULL;
		time->hour_type = SD3078_HOUR_24;
		time->hour   = bcd_hex_convert(buf[2] & 0x3f);
	}
	else                                       
	{
		if ((buf[2] & SD3078_AM_PM_BIT) != 0)
			time->am_pm = SD3078_PM;
		else
			time->am_pm = SD3078_AM;
		time->hour_type = SD3078_HOUR_12;
		time->hour   = bcd_hex_convert(buf[2] & 0x1f);
	}
	time->week  = bcd_hex_convert(buf[3] & 0x07);
	time->day   = bcd_hex_convert(buf[4] & 0x3f);
	time->month = bcd_hex_convert(buf[5] & 0x1f);
	time->year  = bcd_hex_convert(buf[6]);

	return SD3078_EXIT_OK;
}

/**
 * @brief sd3078 set time
 * 
 */
sd3078_exit_code_e sd3078_set_time(sd3078_time_t *time)
{
	unsigned char buf[7];
	sd3078_exit_code_e ret;

	buf[0] = hex_bcd_convert(time->second);
	buf[1] = hex_bcd_convert(time->minute);
	buf[2] = hex_bcd_convert(time->hour);
	if (time->hour_type == SD3078_HOUR_24)  
		buf[2] |= SD3078_12_24_BIT;
	else if (time->am_pm == SD3078_PM)      
		buf[2] |= SD3078_AM_PM_BIT;
	buf[3] = hex_bcd_convert(time->week);
	buf[4] = hex_bcd_convert(time->day);
	buf[5] = hex_bcd_convert(time->month);
	buf[6] = hex_bcd_convert(time->year);

	sd3078_write_enable();
	ret = sd3078_write_reg(SD3078_REG_SEC, 7, buf);
	sd3078_write_disable();
	if (ret != SD3078_EXIT_OK)
		return ret;

	return SD3078_EXIT_OK;
}

sd3078_exit_code_e sd3078_read_alarm_time(sd3078_time_alarm_t *alarm)   
{
	unsigned char buf[8];
	sd3078_exit_code_e ret;

	ret = sd3078_read_reg(SD3078_REG_ALARM_SEC, 8, buf);
	if (ret != SD3078_EXIT_OK)
		return ret;

	alarm->sec_a = bcd_hex_convert(buf[0] & 0x7f);  
	alarm->min_a = bcd_hex_convert(buf[1] & 0x7f);  
	alarm->hour_a = bcd_hex_convert(buf[2] & 0x3f);
	alarm->week_a  = buf[3] & 0x7f;
	alarm->day_a   = bcd_hex_convert(buf[4] & 0x3f);
	alarm->mon_a = bcd_hex_convert(buf[5] & 0x1f);
	alarm->year_a  = bcd_hex_convert(buf[6]);
	alarm->enable_a = buf[7];

	ret = sd3078_read_reg(SD3078_REG_CTR2, 1, buf);     
	if (ret != SD3078_EXIT_OK)
	{
		sd3078_write_disable();
		return ret;
	}
	if(buf[0] & SD3078_INTAE)
		alarm->ie_a = 1;
	else
		alarm->ie_a = 0;
	
	if(buf[0] & SD3078_IM)
		alarm->int_period = 1;
	else
		alarm->int_period = 0;
	
	return SD3078_EXIT_OK;
}

/**
 * @brief   sd3078 set alarm;
 *
 */
sd3078_exit_code_e sd3078_set_alarm_time(sd3078_time_alarm_t *alarm)
{
	sd3078_exit_code_e ret;
	unsigned char buf[8];

	buf[0] = hex_bcd_convert(alarm->sec_a);
	buf[1] = hex_bcd_convert(alarm->min_a);
	buf[2] = hex_bcd_convert(alarm->hour_a);
	buf[3] = alarm->week_a & 0x7f;
	buf[4] = hex_bcd_convert(alarm->day_a);
	buf[5] = hex_bcd_convert(alarm->mon_a);
	buf[6] = hex_bcd_convert(alarm->year_a);
	buf[7] = alarm->enable_a & 0x7f;   										 
	ret = sd3078_write_enable();
	if (ret != SD3078_EXIT_OK)
	{
		return ret ;
	}
	ret = sd3078_write_reg(SD3078_REG_ALARM_SEC, 8, buf);  
	if (ret != SD3078_EXIT_OK)
	{
		sd3078_write_disable();       
		return ret ; 
	}
	
	/*enable interrupt out*/                         
	ret = sd3078_read_reg(SD3078_REG_CTR2, 1, buf);      
	if (ret != SD3078_EXIT_OK)
	{
		sd3078_write_disable();
		return ret;
	}
	buf[0] = buf[0];         			  										
	buf[0] &= ~SD3078_INTS1;       										 
	buf[0] |= SD3078_INTS0;        										 
	
	if(alarm->ie_a == 1)                      				
	{
		buf[0] |= SD3078_INTAE;		                        
	}
	else
	{
		buf[0] &= ~SD3078_INTAE;                         
	}
	if(alarm->int_period == 1)                         
	{
		buf[0] |= SD3078_IM;                          		
	}	
	else
	{
		buf[0] &= ~SD3078_IM;                         			
	}
	ret = sd3078_write_reg(SD3078_REG_CTR2, 1, &buf[0]); 	
	if (ret != SD3078_EXIT_OK)
	{
		sd3078_write_disable();
		return ret ;
	}
	sd3078_write_disable();
	return SD3078_EXIT_OK;
}

/**
 * @brief  get sd3078 chip history temperature
 * 
 */
sd3078_exit_code_e sd3078_get_his_temperature(signed char *temp, sd3078_temp_cmd_e cmd)   
{
	sd3078_exit_code_e ret;
	unsigned reg;
	unsigned char buf;

	if(cmd == SD3078_TEMP_HIS_L)
		reg = SD3078_REG_HIS_L;
	else if(cmd == SD3078_TEMP_HIS_H)
		reg = SD3078_REG_HIS_H;
	else
		return  SD3078_EXIT_ARG_ERR;

	ret = sd3078_read_reg(reg, 1, &buf);
	if (ret != SD3078_EXIT_OK)
		return ret ;
	*temp = (signed  char)buf;
	return SD3078_EXIT_OK;
}

/**
 * @brief  get sd3078 chip temperature
 * 
 */
sd3078_exit_code_e sd3078_get_temperature(signed char *temp)
{
	sd3078_exit_code_e ret;
	unsigned char buf;

	ret = sd3078_read_reg(SD3078_REG_TEMP, 1, &buf);   
	if (ret != SD3078_EXIT_OK)
		return ret ;
	*temp = (signed  char)buf;
	return SD3078_EXIT_OK;
}

/**
 * @brief   sd3078 set when  power supply by battery i2c work state,
 * en:0,battery supply i2c is not work,1, battery supply i2c is  work
 * 
 */
sd3078_exit_code_e sd3078_battery_i2c_ctrl(unsigned char en)
{
	sd3078_exit_code_e ret;
	unsigned char buf;

	if(en != 0 && en != 1)     
		return SD3078_EXIT_ARG_ERR;
	if(en)
		buf = 0x80;
	else
		buf =0x00;

	sd3078_write_enable();
	ret = sd3078_write_reg(SD3078_REG_I2C_CTRL, 1, &buf);   
	sd3078_write_disable();
	if (ret != SD3078_EXIT_OK)
		return ret ;
	return SD3078_EXIT_OK;
}

/**
 * @brief   sd3078 read battery voltage
 * 
 */
sd3078_exit_code_e sd3078_read_battery_voltage(unsigned short *vol)
{
	sd3078_exit_code_e ret;
	unsigned char buf[2];

	ret = sd3078_read_reg(SD3078_REG_CTR5, 2, buf);
	if (ret != SD3078_EXIT_OK)
		return ret ;
	*vol = (((buf[0] & 0x80 ) << 1) | buf[1]);

	return SD3078_EXIT_OK;
}

/**
 * @brief   sd3078 read battery charge control
 * 
 */
sd3078_exit_code_e sd3078_battery_charge_ctrl(sd3078_charge_t *ctrl)
{
	unsigned char buf;
	sd3078_exit_code_e ret;

	if (ctrl->chage_en > 1)    
		return SD3078_EXIT_ARG_ERR;
	if (ctrl->resistance > 3)   
		return SD3078_EXIT_ARG_ERR;
	
	buf = (ctrl->chage_en <<7) | ctrl->resistance;
	sd3078_write_enable();
	ret = sd3078_write_reg(SD3078_REG_CHARGE, 1, &buf);   
	sd3078_write_disable();
	if (ret != SD3078_EXIT_OK)
		return ret ;
	return SD3078_EXIT_OK;
}

/**
 * @brief  sd3078 read temp reg
 *	
 */
sd3078_exit_code_e sd3078_get_Alarm_temp(signed char *Temp,sd3078_temp_cmd_e cmd)
{
	unsigned char buf;
	sd3078_exit_code_e ret;
	
	if(cmd == SD3078_TEMP_AH)
	{
		ret = sd3078_read_reg(SD3078_REG_TEMP_AH, 1, &buf);
		if (ret != SD3078_EXIT_OK)
		return ret ;
	}
	
	else if(cmd == SD3078_TEMP_AL)
	{
		ret = sd3078_read_reg(SD3078_REG_TEMP_AL, 1, &buf);
		if (ret != SD3078_EXIT_OK)
		return ret ;
	}
	*Temp = (signed char)buf;
	return SD3078_EXIT_OK;
}


/**
 * @brief   sd3078 set temperature alarm value;
 * cmd:0:set lowest temperature alarm;1:set highest temperature alarm
 * 
 */
sd3078_exit_code_e sd3078_set_alarm_temp(sd3078_temp_alarm_t *temp,  sd3078_temp_cmd_e cmd)
{
	unsigned char buf;
	sd3078_exit_code_e ret;
	unsigned char reg;
	unsigned char oe_bit;

	if(cmd == SD3078_TEMP_AL) 
	{
		reg = SD3078_REG_TEMP_AL;     
		oe_bit = SD3078_TEMP_AL_OE;  
	}
		
	else if(cmd == SD3078_TEMP_AH)  
	{
		reg = SD3078_REG_TEMP_AH;			
		oe_bit = SD3078_TEMP_AH_OE;		
	}
	else
		return SD3078_EXIT_ARG_ERR; 

	sd3078_write_enable();
	buf = (unsigned char)temp->temp;  
	sd3078_write_reg(reg, 1, &buf);    
	
	ret = sd3078_read_reg(SD3078_REG_CTR4, 1, &buf);   
	if (ret != SD3078_EXIT_OK)
		return ret ;
	/*enable temperature alarm*/
	if (temp->oe == 1)   
	{
		buf  |= oe_bit;   
	}
	else
	{
		buf &= (~oe_bit);
	}
	ret = sd3078_write_reg(SD3078_REG_CTR4, 1, &buf);   
	sd3078_write_disable();
	if (ret != SD3078_EXIT_OK)
		return ret ;
	return SD3078_EXIT_OK;
}

/**
 * @brief sd3078 historical minimum / maximum temperature occurrence time
 *
 */
sd3078_exit_code_e sd3078_get_his_temp_time(sd3078_time_t *time, sd3078_temp_cmd_e cmd)
{
	sd3078_exit_code_e ret;
	unsigned char buf[7];
	unsigned char reg;

	if(cmd == SD3078_TEMP_HIS_L)       
		reg = SD3078_REG_HIS_L_MIN;     
	else if(cmd == SD3078_TEMP_HIS_H)  
		reg = SD3078_REG_HIS_H_MIN;			 
	else
		return SD3078_EXIT_ARG_ERR;
	
	ret = sd3078_read_reg(reg, 6, buf);
	if (ret != SD3078_EXIT_OK)
		return ret ;
	
	time->second = 0;
	time->minute = bcd_hex_convert(buf[0] & 0x7f);

	if ((buf[1] & SD3078_12_24_BIT) != 0)
	{
		time->am_pm = SD3078_AM_PM_NULL;
		time->hour_type = SD3078_HOUR_24;
		time->hour   = bcd_hex_convert(buf[1] & 0x3f);
	}
	else 
	{
		if ((buf[1] & SD3078_AM_PM_BIT) != 0)
			time->am_pm = SD3078_PM;
		else
			time->am_pm = SD3078_AM;
		time->hour_type = SD3078_HOUR_12;
		time->hour   = bcd_hex_convert(buf[1] & 0x1f);
	}
	time->week   = bcd_hex_convert(buf[2] & 0x07);
	time->day    = bcd_hex_convert(buf[3] & 0x3f);
	time->month  = bcd_hex_convert(buf[4] & 0x1f);
	time->year   = bcd_hex_convert(buf[5]);
	return SD3078_EXIT_OK;
}

/**
 * @brief   sd3078 read user ram;
 *
 */
sd3078_exit_code_e sd3078_read_ram(sd3078_ram_t *ram)
{
	sd3078_exit_code_e ret;

	if(ram->st_addr < SD3078_REG_USER_RAM_START ||
	   ram->end_addr > SD3078_REG_USER_RAM_END ||
	   ram->end_addr < ram->st_addr)
		return SD3078_EXIT_ARG_ERR;
	
	ret = sd3078_read_reg(ram->st_addr,  ram->end_addr - ram->st_addr + 1, ram->buf);
	if (ret != SD3078_EXIT_OK)
	{
		return ret ;
	}
	return SD3078_EXIT_OK;
}

/**
 * @brief   sd3078 write user ram;
 *
 */
sd3078_exit_code_e sd3078_write_ram(sd3078_ram_t *ram)
{
	sd3078_exit_code_e ret;

	if(ram->st_addr < SD3078_REG_USER_RAM_START ||
	   ram->end_addr > SD3078_REG_USER_RAM_END ||
	   ram->end_addr < ram->st_addr)     
		return SD3078_EXIT_ARG_ERR;
	
	sd3078_write_enable();
	ret = sd3078_write_reg(ram->st_addr, ram->end_addr - ram->st_addr + 1, ram->buf);
	sd3078_write_disable();
	if (ret != SD3078_EXIT_OK)
		return ret ;
	return SD3078_EXIT_OK;
}

/**
 * @brief   sd3078 read device id;
 *
 */
sd3078_exit_code_e sd3078_read_device_id(unsigned char *buf)
{
	sd3078_exit_code_e ret;

	ret = sd3078_read_reg(SD3078_REG_DEVICE_ID, 8, buf);
	if (ret != SD3078_EXIT_OK)
		return ret ;
	return SD3078_EXIT_OK;
}

/**
 * @brief   sd3078 clock out
 *
 */
sd3078_exit_code_e sd3078_clk_out(sd3078_clk_out_t *clk)
{
	sd3078_exit_code_e ret;
	unsigned char buf[2];

	if(clk->freq > SD3078_CLK_OUT_1_SEC)   
		return SD3078_EXIT_ARG_ERR;

	ret = sd3078_write_enable();
	if (ret != SD3078_EXIT_OK)
	{
		return ret ;
	}
	ret = sd3078_read_reg(SD3078_REG_CTR2, 2, buf);
	if (ret != SD3078_EXIT_OK)
	{
		sd3078_write_disable();
		return ret ;
	}

	buf[1] &= 0xf0;
	buf[1] |= clk->freq;    
	if(clk->oe == 1)
	{
		buf[0] |= SD3078_INTFE;   
	}
	else
	{
		buf[0] &= ~SD3078_INTFE;
	}
	buf[0] |= SD3078_INTS1;     
	buf[0] &= ~SD3078_INTS0;
	
	ret = sd3078_write_reg(SD3078_REG_CTR2, 2, buf);
	sd3078_write_disable();
	if (ret != SD3078_EXIT_OK)
	{
		return ret ;
	}
	return SD3078_EXIT_OK;
}

/**
 * @brief   sd3078  count down
 *
 */
sd3078_exit_code_e sd3078_count_down(sd3078_count_down_t *count_down)
{
	sd3078_exit_code_e ret;
	unsigned char buf[3];

	if(count_down->count > 0xffffff)      
		return SD3078_EXIT_ARG_ERR;
	if(count_down->source > SD3078_COUNT_DOWN_1_60)  
		return SD3078_EXIT_ARG_ERR;

	
	ret = sd3078_write_enable();
	if (ret != SD3078_EXIT_OK)
	{
		return ret;
	}
	ret = sd3078_read_reg(SD3078_REG_CTR2, 2, buf);
	if (ret != SD3078_EXIT_OK)
	{
		sd3078_write_disable();
		return ret;
	}
	
	/*set INTDE =0*/
	buf[0] &= ~SD3078_INTDE;   
	ret = sd3078_write_reg(SD3078_REG_CTR2, 1, &buf[0]);
	if (ret != SD3078_EXIT_OK)
	{
		sd3078_write_disable();
		return ret;
	}
	
	if (count_down->ie == 1)   
	{
		buf[0] |= SD3078_INTDE;    
		buf[0] |= SD3078_INTS1;   
		buf[0] |= SD3078_INTS0;		 
	}

	if (count_down->int_period == 1)
	{
		buf[0] |= SD3078_IM;    
	}
	else
	{
		buf[0] &= ~SD3078_IM;  
	}
	buf[1] &=  ~(0x03 << 4);   
	buf[1] |=  (count_down->source  << 4);  
	ret = sd3078_write_reg(SD3078_REG_CTR2, 2, buf);  
	if (ret != SD3078_EXIT_OK)
	{
		sd3078_write_disable();
		return ret;
	}
	buf[0] = count_down->count & 0xff;
	buf[1] = (count_down->count >> 8 ) & 0xff;
	buf[2] = (count_down->count >> 16 ) & 0xff;
	ret = sd3078_write_reg(SD3078_REG_TD0, 3, buf);   
	sd3078_write_disable();
	if (ret != SD3078_EXIT_OK)
	{
		return ret;
	}
	return SD3078_EXIT_OK;
}

/**
 * @brief   sd3078  clear flag
 *
 */
sd3078_exit_code_e sd3078_flag_clear(sd3078_flag_e flag)
{
	sd3078_exit_code_e ret;
	unsigned char buf;

	ret = sd3078_write_enable();
	if (ret != SD3078_EXIT_OK)
	{
		return ret;
	}
	
	switch (flag)
	{
		case SD3078_OSF_FLAG:
			buf = ~0x40;
			break;
		case SD3078_ALARM_TIME_FLAG://alarm time flag
			buf = ~0x20;
			break;
		case SD3078_COUNT_DWON_FLAG: //count down flag
			buf = ~0x10;
			break;
		case SD3078_BLF_FLAG:
			buf = ~0x08;
			break;
		case SD3078_PMF_FLAG:
			buf = ~0x02;
			break;
		case SD3078_RTCF_FLAG:
			buf = ~0x01;
			break;
		default:
			return SD3078_EXIT_ARG_ERR;
	}
	buf |= 0x84;
	ret = sd3078_write_reg(SD3078_REG_CTR1, 1, &buf);  
	if (ret != SD3078_EXIT_OK)
	{
		return ret;
	}
	sd3078_write_disable();
	return SD3078_EXIT_OK;
}

/**
 * @brief   sd3078  get flag
 *
 */
sd3078_exit_code_e sd3078_flag_get(sd3078_flag_e flag, unsigned char *value)
{
	sd3078_exit_code_e ret;
	unsigned char buf;

	ret = sd3078_read_reg(SD3078_REG_CTR1, 1, &buf);  
	if (ret != SD3078_EXIT_OK)
	{
		return ret ;
	}
	
	switch (flag)
	{
		case SD3078_OSF_FLAG:
			buf &= 0x40;
			break;
		case SD3078_ALARM_TIME_FLAG://alarm time flag
			buf &= 0x20;
			break;
		case SD3078_COUNT_DWON_FLAG: //count down flag
			buf &= 0x10;
			break;
		case SD3078_BLF_FLAG:
			buf &= 0x08;
			break;
		case SD3078_PMF_FLAG:
			buf &= 0x02;
			break;
		case SD3078_RTCF_FLAG:
			buf &= 0x01;
			break;
		default:
			return SD3078_EXIT_ARG_ERR;
	}
	if (buf == 0)
		*value = 0;
	else
		*value = 1;
	return SD3078_EXIT_OK;
}
